iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
1
自我挑戰組

30天學python系列 第 19

[Day19] Python 語言進階 - 3

  • 分享至 

  • xImage
  •  

函數的使用方式

  • 將函數視為'一等公民'
    • 函數可以賦值給變數
    • 函數可以作為函數的參數
    • 函數可以作為函數的返回值
  • 高階函數的用法(filter、map以及它們的替代品)
items1 =  list ( map ( lambda  x : x **  2 , filter ( lambda  x : x %  2 , range ( 1 , 10 )))) 
items2 = [x **  2  for x in  range ( 1 , 10 ) if x %  2 ]
  • 位置參數、可變參數、關鍵字參數、命名關鍵字參數
  • 參數的元訊息(
  • 匿名函數和內聯函數的用法(lambda 函數)
  • 閉包 (closure) 和作用域問題
    • Python 索引變數的 LEGB 順序 (Local --> Embedded --> Global --> Built-in)
    • global 和 nonlocal 關鍵字的作用
      global:聲明或定義全域變數
      nonlocal:聲明使用嵌套作用域的變數。
  • 裝飾器函數(使用裝飾器 (decorator) 和取消裝飾器)
    範例 - 輸出函數執行時間的裝飾器。
from functools import wraps
from time import time

def record_time(func): # 定義裝飾函数的裝飾器
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time()
        result = func(*args, **kwargs)
        print(f'{func.__name__} : {time() - start} 秒')
        return result
        
    return wrapper
@record_time
def test():
    pass

if __name__ == "__main__":
    test()

https://ithelp.ithome.com.tw/upload/images/20191001/20121116UBRF1bwqbV.png
如果裝飾器不希望跟 print 函數耦合,可以編寫帶參數的裝飾器。

from functools import wraps
from time import time
import sys
def record(output):		# 定義帶參數 (函數) 的裝飾器
	def decorate(func):
		@wraps(func)
		def wrapper(*args, **kwargs):
			start = time()
			result = func(*args, **kwargs)
			output(func.__name__, time() - start)
			return result
		return wrapper
	return decorate

def out(str,float):
	print(f'{str} : {float} 秒')

@record(output = out)
def test():
	pass

if __name__ == "__main__":
    test()

定義裝飾類別(利用 call 方法使得物件可以當成函數調用)

from functools import wraps
from time import time

class Record():

    def __init__(self, output):
        self.output = output

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time()
            result = func(*args, **kwargs)
            self.output(func.__name__, time() - start)
            return result
        return wrapper

def out(str,float):
	print(f'{str} : {float} 秒')

@Record(out)
def test():
    pass

if __name__ == "__main__":
    test()

以上兩種也會得到此結果。
https://ithelp.ithome.com.tw/upload/images/20191001/201211169WO08TEHfr.png
範例 - 用裝飾器來實現單例模式 (Singleton)。
在應用這個模式時,單例對象的類別必須保證只有一個實例存在。
許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。
例如在伺服器程序中,該伺服器的配置數據存放在一個文件中,這些配置數據由一個單例物件統一讀取,然後服務行程 (process) 中的其他對象再通過這個單例獲取這些配置數據。

from functools import wraps

def singleton(cls):     # 裝飾類別的裝飾器
    instances = {}
    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class test():      # 類別
    pass

t1 = test()
t2 = test()

print(t1)
print(t2)

可以看出,雖然進行兩次實例化,但仍為同一個。
https://ithelp.ithome.com.tw/upload/images/20191001/20121116WyuTURrO1J.png
單例模式在多線程 (Multithreading) 的應用場合必須小心使用。如果當唯一實例尚未創建時,有兩個線程 (thread) 同時調用創建方法,它們同時沒有檢測到唯一實例的存在,從而同時各自創建了一個實例,這樣就有兩個實例被構造出來,違反了單例模式中實例唯一的原則。
線程安全的單例裝飾器。 解決這個問題的辦法是為已經實例化的變數提供一個互斥鎖。

from functools import wraps
from threading import Lock

def singleton(cls):		# 裝飾類別的裝飾器
    instances = {}
    locker = Lock()
    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instances:
            with locker:
                if cls not in instances:
                    instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class test():      # 類別
    pass

上一篇
[Day18] Python 語言進階 - 2
下一篇
[Day20] Python 語言進階 - 4
系列文
30天學python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言